﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BPMS.Core.DataAccess;
using BPMS.Core.Builders;
using System.Data.Common;
using BPMS.Utility.Reflections;
using System.Data;
using BPMS.Utility.Converter;

namespace BPMS.Core
{
    public class DbContext : IDbContext
    {
        private readonly DbHelper dbHelper;
        private bool isDisposed = false;

        public DbContext()
        {
            dbHelper = new DbHelper();
        }

        public DbContext(ConnectionInfo connectionInfo)
        {
            dbHelper = new DbHelper(connectionInfo);
        }

        private void Dispose(bool isDisposing)
        {
            if (isDisposing)
            {
                if (!isDisposed)
                {
                    dbHelper.Dispose();
                    isDisposed = true;
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public int Update<TEntity>(TEntity entity) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>(BuilderType.Update).GetOperateByKeyStatement();
            return dbHelper.ExecuteNonQuery(strSql, DbParameterFactory.Instance.KeyParameters(entity));
        }

        public int Update<TEntity>(TEntity entity, object parameters) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>(BuilderType.Update).GetOperateByConditionObjectStatement(parameters);
            return dbHelper.ExecuteNonQuery(strSql, DbParameterFactory.Instance.InParameters(parameters));
        }

        public int Update<TEntity>(TEntity entity, string whereClause, object parameters) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>(BuilderType.Update).GetOperateByConditionStringStatement(whereClause);
            return dbHelper.ExecuteNonQuery(strSql, DbParameterFactory.Instance.InParameters(parameters));
        }

        public int Update<TEntity>(object setClause, object parameters) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>(setClause).GetOperateByConditionObjectStatement(parameters);
            return dbHelper.ExecuteNonQuery(strSql, DbParameterFactory.Instance.InParameters(parameters));
        }

        public int Update<TEntity>(object setClause, string whereClause, object parameters) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>(setClause).GetOperateByConditionStringStatement(whereClause);
            return dbHelper.ExecuteNonQuery(strSql, DbParameterFactory.Instance.InParameters(parameters));
        }

        public int Delete<TEntity>(TEntity entity) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>(BuilderType.Delete).GetOperateByKeyStatement();
            return dbHelper.ExecuteNonQuery(strSql, DbParameterFactory.Instance.KeyParameters(entity));
        }

        public int Delete<TEntity>(object parameters) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>(BuilderType.Delete).GetOperateByConditionObjectStatement(parameters);
            return dbHelper.ExecuteNonQuery(strSql, DbParameterFactory.Instance.InParameters(parameters));
        }

        public int Delete<TEntity>(string whereClause, object parameters) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>(BuilderType.Delete).GetOperateByConditionStringStatement(whereClause);
            return dbHelper.ExecuteNonQuery(strSql, DbParameterFactory.Instance.InParameters(parameters));
        }

        public TResult Identity<TEntity, TResult>(TEntity entity) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>(BuilderType.Insert).GetOperateStatement();
            return (TResult)Convert.ChangeType(dbHelper.ExecuteIdentity(strSql), typeof(TResult));
        }

        public bool Insert<Tentity>(Tentity entity) where Tentity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<Tentity>(BuilderType.Insert).GetOperateStatement();
            return dbHelper.ExecuteNonQuery(strSql, DbParameterFactory.Instance.InParameters(entity)) > 0;
        }

        public TEntity QuerySingle<TEntity>(object parameters) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>().GetOperateByConditionObjectStatement(parameters);
            return dbHelper.ExecuteReaderSingle<TEntity>(strSql, DbParameterFactory.Instance.InParameters(parameters));
        }

        public TEntity QuerySingle<TEntity>(string whereClause, object parameters) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>().GetOperateByConditionStringStatement(whereClause);
            return dbHelper.ExecuteReaderSingle<TEntity>(strSql, DbParameterFactory.Instance.InParameters(parameters));
        }

        public IEnumerable<TEntity> QueryList<TEntity>(object parameters) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>().GetOperateByConditionObjectStatement(parameters);
            return dbHelper.ExecuteReader<TEntity>(strSql, DbParameterFactory.Instance.InParameters(parameters));
        }

        public IEnumerable<TEntity> QueryList<TEntity>(string whereClause, object parameters) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>().GetOperateByConditionStringStatement(whereClause);
            return dbHelper.ExecuteReader<TEntity>(strSql, DbParameterFactory.Instance.InParameters(parameters));
        }

        public dynamic QuerySingle(string sql)
        {
            return dbHelper.ExecuteReaderSingle<DataRecordDynamic>(sql);
        }

        public dynamic QuerySingle(string sql, object parameters)
        {
            return dbHelper.ExecuteReaderSingle<DataRecordDynamic>(sql, DbParameterFactory.Instance.InParameters(parameters));
        }

        public IEnumerable<dynamic> QueryList(string sql)
        {
            DataTable resultTable = dbHelper.ExecuteDataTable(sql);
            if (null != resultTable)
            {
                foreach (DataRow item in resultTable.Rows)
                {
                    yield return new DataRowDynamic(item);
                }
            }
        }

        public IEnumerable<dynamic> QueryList(string sql, object parameters)
        {
            DataTable resultTable = dbHelper.ExecuteDataTable(sql, DbParameterFactory.Instance.InParameters(parameters));
            if (null != resultTable)
            {
                foreach (DataRow item in resultTable.Rows)
                {
                    yield return new DataRowDynamic(item);
                }
            }
        }

        public int ExecuteSql(string sql)
        {
            return dbHelper.ExecuteNonQuery(sql);
        }

        public int ExecuteSql(string sql, object parameters)
        {
            return dbHelper.ExecuteNonQuery(sql, DbParameterFactory.Instance.InParameters(parameters));
        }

        public int ExecuteProc(string procedure, object parameters, object outParameters)
        {
            IEnumerable<DbParameterWrapper> allParameters = DbParameterFactory.Instance.InParameters(parameters).Union(DbParameterFactory.Instance.OutParameters(outParameters));
            return dbHelper.ExecuteNonQuery(procedure, allParameters, System.Data.CommandType.StoredProcedure);
        }

        public IEnumerable<TEntity> Page<TEntity>(PageData pageData, object parameters) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>().GetOperateByConditionObjectStatement(parameters);
            DataSet pageDataSet = dbHelper.ExecuteDataSet(new PageQueryStatementBuilder(strSql).BuildPageQuerySql(), DbParameterFactory.Instance.PageParameters(pageData.PageIndex, pageData.PageSize, parameters));
            if (null != pageDataSet && pageDataSet.Tables.Count >= 2)
            {
                pageData.RecordCount = TypeParser.ToInt32(pageDataSet.Tables[0].Rows[0][0]);
                foreach (DataRow rowItem in pageDataSet.Tables[1].Rows)
                {
                    yield return (TEntity)ReflectionUtility.CreateInstance<TEntity>(rowItem);
                }
            }
        }

        public IEnumerable<TEntity> Page<TEntity>(PageData pageData, string whereClause, object parameters) where TEntity : EntityBase
        {
            string strSql = SqlBuilderFactory.CreateBuilder<TEntity>().GetOperateByConditionStringStatement(whereClause);
            DataSet pageDataSet = dbHelper.ExecuteDataSet(new PageQueryStatementBuilder(strSql).BuildPageQuerySql(), DbParameterFactory.Instance.PageParameters(pageData.PageIndex, pageData.PageSize, parameters));
            if (null != pageDataSet && pageDataSet.Tables.Count >= 2)
            {
                pageData.RecordCount = TypeParser.ToInt32(pageDataSet.Tables[0].Rows[0][0]);
                foreach (DataRow rowItem in pageDataSet.Tables[1].Rows)
                {
                    yield return (TEntity)ReflectionUtility.CreateInstance<TEntity>(rowItem);
                }
            }
        }

        public IEnumerable<dynamic> Page(PageData pageData, string sql)
        {
            DataSet pageDataSet = dbHelper.ExecuteDataSet(new PageQueryStatementBuilder(sql).BuildPageQuerySql(), DbParameterFactory.Instance.PageParameters(pageData.PageIndex, pageData.PageSize, null));
            if (null != pageDataSet && pageDataSet.Tables.Count >= 2)
            {
                pageData.RecordCount = TypeParser.ToInt32(pageDataSet.Tables[0].Rows[0][0]);
                foreach (DataRow rowItem in pageDataSet.Tables[1].Rows)
                {
                    yield return new DataRowDynamic(rowItem);
                }
            }
        }

        public IEnumerable<dynamic> Page(PageData pageData, string sql, object parameters)
        {
            DataSet pageDataSet = dbHelper.ExecuteDataSet(new PageQueryStatementBuilder(sql).BuildPageQuerySql(), DbParameterFactory.Instance.PageParameters(pageData.PageIndex, pageData.PageSize, parameters));
            if (null != pageDataSet && pageDataSet.Tables.Count >= 2)
            {
                pageData.RecordCount = TypeParser.ToInt32(pageDataSet.Tables[0].Rows[0][0]);
                foreach (DataRow rowItem in pageDataSet.Tables[1].Rows)
                {
                    yield return new DataRowDynamic(rowItem);
                }
            }
        }
    }
}
